
package com.jprocms.module.cms.controller.admin.sitetpl;

import cn.hutool.core.collection.CollUtil;
import com.jpro.framework.common.exception.ServerException;
import com.jpro.framework.common.pojo.CommonResult;
import com.jprocms.module.cms.controller.admin.sitetpl.dto.TplDeleteDto;
import com.jprocms.module.cms.controller.admin.sitetpl.dto.TplReSourceDto;
import com.jprocms.module.cms.controller.admin.sitetpl.dto.TplRenameDto;
import com.jprocms.module.cms.controller.admin.sitetpl.dto.TplSaveDirDto;
import com.jprocms.module.cms.controller.admin.sitetpl.vo.SiteTplListVo;
import com.jprocms.module.cms.controller.admin.sitetpl.vo.SiteTplTreeVo;
import com.jprocms.module.cms.convert.sitetpl.SiteTplConvert;
import com.jprocms.module.cms.dal.dataobject.site.SiteDO;
import com.jprocms.module.cms.framework.annotation.DecodeBase64Anno;
import com.jprocms.module.cms.service.sitetpl.IResFile;
import com.jprocms.module.cms.service.sitetpl.TplService;
import com.jprocms.module.cms.util.CmsContextUtil;
import com.jprocms.module.cms.util.FileUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

import static com.jpro.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.jpro.framework.common.pojo.CommonResult.success;
import static com.jprocms.module.cms.constant.CmsSysConstants.SPT;
import static com.jprocms.module.cms.enums.ErrorCodeConstants.*;

/**
 * @author jprocms
 */
@Slf4j
@Tag(name = "管理后台 - 站点资源管理")
@RestController
@RequestMapping("/cms/siteres")
@Validated
public class ResourceController extends AbstractTplAndResourceController {


    /**
     * 资源树
     * @return ResponseInfo
     * @throws ServerException 异常
     */
    @GetMapping(value = "/tree")
    @Operation(summary = "左侧资源树")
    @PreAuthorize("@ss.hasPermission('cms:res:list')")
    public CommonResult<List<SiteTplTreeVo>> tree(String root) throws ServerException {
        SiteDO site = CmsContextUtil.getSite();
        if (StringUtils.isBlank(root)) {
            root = site.getResourcePath();
        } else {
            FileUtil.isValidFilename(site.getResourcePath(), root);
        }
        try {
            List<IResFile> list = tplService.listFile(root, true);
            list = list.stream().sorted(Comparator.comparing(IResFile::getFilename)).collect(Collectors.toList());
            List<SiteTplTreeVo> vos = CollUtil.newArrayList();
            SiteTplConvert.INSTANCE.convertTplResTree(vos, list);
            List<SiteTplTreeVo> emptyList = Collections.emptyList();
            for (SiteTplTreeVo vo : vos) {
                vo.setChildren(emptyList);
            }
            return success(vos);
        } catch (IOException e) {
            throw exception(FILE_NOT_EXISTS);
        }
    }

    /**
     * 资源列表
     *
     * @param root 路径
     * @return ResponseInfo
     * @throws ServerException 异常
     */
    @GetMapping(value = "/list")
    @Operation(summary = "资源列表")
    @PreAuthorize("@ss.hasPermission('cms:res:list')")
    public CommonResult<List<SiteTplListVo>> resourceList(String root, String filename) throws ServerException {
        SiteDO site = CmsContextUtil.getSite();
        if (StringUtils.isBlank(root)) {
            root = site.getResourcePath();
        } else {
            FileUtil.isValidFilename(site.getResourcePath(), root);
        }
        try {
            List<IResFile> list = tplService.listFile(root, false);
            if (StringUtils.isNotBlank(filename)) {
                list = list.stream().filter(x -> x.getFilename().contains(filename)).collect(Collectors.toList());
            }
            List<SiteTplListVo> siteTplListVos = SiteTplConvert.INSTANCE.convertTplResList(list);
            return success(siteTplListVos);
        } catch (IOException e) {
            throw exception(FILE_NOT_EXISTS);
        }
    }

    /**
     * 添加资源目录
     *
     * @param dto    资源目录Dto
     * @param result BindingResult
     * @return ResponseInfo
     * @throws ServerException 异常
     */
    @Operation(summary = "新建资源目录")
    @PreAuthorize("@ss.hasPermission('cms:res:create')")
    @PostMapping(value = "/dir")
    public CommonResult createDir(@Valid @RequestBody TplSaveDirDto dto, BindingResult result) throws ServerException {
        validateBindingResult(result);
        SiteDO site = CmsContextUtil.getSite();
        String root = dto.getRoot();
        if (StringUtils.isBlank(root)) {
            root = site.getResourcePath();
        }
        validSave(root + SPT + dto.getDirName(), site.getResourcePath());
        try {
            if (StringUtils.isBlank(dto.getRoot())) {
                dto.setRoot(site.getResourcePath());
            }
            tplService.saveDir(dto);
        } catch (IOException e) {
            throw exception(FILE_CREATE_ERROR);
        }
        return success(true);
    }

    /**
     * 校验文件是否唯一（修改）
     *
     * @param filename 目录名称
     * @param root     目录路径
     * @return true 唯一 false 不唯一
     */
    @Operation(summary = "检查是否唯一")
    @PreAuthorize("@ss.hasPermission('cms:res:list')")
    @GetMapping(value = "/unique/filename")
    public CommonResult checkByFilename(@NotBlank @RequestParam String filename, @NotBlank @RequestParam String root,
                                        HttpServletRequest request) throws ServerException {
        SiteDO site = CmsContextUtil.getSite();
        if (root == null) {
            root = site.getResourcePath();
        }
        return success(!vldExistFile(root + SPT + filename));
    }

    /**
     * 读取资源文件信息
     *
     * @param name 资源路径
     * @return ResponseInfo
     * @throws ServerException 异常
     */
    @Operation(summary = "读取资源文件信息")
    @PreAuthorize("@ss.hasPermission('cms:res:list')")
    @GetMapping(value = "/detail")
    public CommonResult<IResFile> get(String name) throws ServerException, IOException {
        SiteDO site = CmsContextUtil.getSite();
        validBlank(name);
        validFileExist(name, site.getResourcePath());
        return success(tplService.get(name));
    }

    /**
     * 添加资源文件
     *
     * @param resDto 资源文件Dto
     * @return ResponseInfo
     * @throws ServerException 异常
     */
    @Operation(summary = "添加资源文件")
    @DecodeBase64Anno
    @PreAuthorize("@ss.hasPermission('cms:res:create')")
    @PostMapping(value = "/save")
    public CommonResult save(@Valid @RequestBody TplReSourceDto resDto) throws ServerException, IOException {
        validBlank(resDto.getFilename(), resDto.getSource());
        String root = resDto.getRoot();
        SiteDO site = CmsContextUtil.getSite();
        if (StringUtils.isBlank(root)) {
            root = site.getResourcePath();
        }
        // 验证参数
        validSave(root + SPT + resDto.getFilename(), site.getResourcePath());
        if (StringUtils.isBlank(resDto.getRoot())) {
            resDto.setRoot(site.getResourcePath());
        }
        tplService.saveFile(resDto);
        return success(true);
    }

    /**
     * 修改资源文件
     *
     * @param updateDto 修改文件Dto
     * @param request   HttpServletRequest
     * @param result    BindingResult
     * @return ResponseInfo
     * @throws ServerException 异常
     */
    @Operation(summary = "修改资源文件")
    @DecodeBase64Anno
    @PreAuthorize("@ss.hasPermission('cms:res:update')")
    @PostMapping(value = "/update")
    public CommonResult update(@Valid @RequestBody TplReSourceDto updateDto, HttpServletRequest request,
                               BindingResult result) throws Exception {
        validateBindingResult(result);
        SiteDO site = CmsContextUtil.getSite();
        String oldName = updateDto.getRoot() + SPT + updateDto.getOriginFileName();
        String newName = updateDto.getRoot() + SPT + updateDto.getFilename();
        String oldSuffix = oldName.substring(oldName.lastIndexOf(".") + 1);
        //判断文件名后缀是否js或css结尾
        if (!SiteTplListVo.EDIT_RE_SUFFIX.contains(oldSuffix)) {
            log.error("文件名或者路径是否非法的");
            exception(RES_FILE_EDIT_NOT_SUPPORT);
        }
        if (newName.equalsIgnoreCase(oldName)) {
            //如果新名称和旧名称一样，只判断原文件存在与否
            validFileExist(oldName, site.getResourcePath());
        } else {
            //判断文件名后缀是否js或css结尾
            String newSuffix = newName.substring(newName.lastIndexOf(".") + 1);
            if (!SiteTplListVo.EDIT_RE_SUFFIX.contains(oldSuffix)) {
                log.error("文件名或者路径是否非法的");
                exception(RES_FILE_EDIT_NOT_SUPPORT);
            }
            //如果新名称和旧名称不一样，需要判断原文件和旧文件存在与否
            validReName(oldName, newName, site.getResourcePath());
        }
        //拼接上相对路径 统一service调用 模板和资源可以共用一个方法
        updateDto.setOriginFileName(oldName);
        updateDto.setFilename(newName);
        tplService.update(updateDto);
        return success(true);
    }

    /**
     * 删除资源文件
     *
     * @param nameDto 模板资源删除Dto
     * @param result  BindingResult
     * @param request HttpServletRequest
     * @return ResponseInfo
     * @throws ServerException 异常
     */
    @SneakyThrows
    @Operation(summary = "删除资源文件")
    @PreAuthorize("@ss.hasPermission('cms:res:delete')")
    @PostMapping("/delete")
    public CommonResult delete(@Valid TplDeleteDto nameDto, BindingResult result,
                               HttpServletRequest request) throws ServerException {
        validateBindingResult(result);
        SiteDO site = CmsContextUtil.getSite();
        /* 验证名字是否合法路径 */
        for (String n : nameDto.getNames()) {
            //检查操作是否再指定目录下
            FileUtil.isValidFilename(site.getResourcePath(), n);
        }
        tplService.delete(nameDto.getNames());
        return success(true);
    }

    /**
     * 修改资源文件名称
     *
     * @param renameDto 模板、资源重命名Dto
     * @param request   HttpServletRequest
     * @param result    BindingResult
     * @return ResponseInfo
     * @throws ServerException 异常
     */
    @Operation(summary = "修改模板名称")
    @PreAuthorize("@ss.hasPermission('cms:tpl:update')")
    @PostMapping("/rename")
    public CommonResult rename(@Valid @RequestBody TplRenameDto renameDto, HttpServletRequest request,
                               BindingResult result) throws ServerException {
        validateBindingResult(result);
        SiteDO site = CmsContextUtil.getSite();
        validReName(renameDto.getFileName(), renameDto.getNewName(), site.getResourcePath());
        tplService.rename(renameDto.getFileName(), renameDto.getNewName());
        return success(true);
    }

    /**
     * 上传资源文件
     *
     * @param file    文件
     * @param root    路径
     * @param request HttpServletRequest
     * @return ResponseInfo
     * @throws ServerException 异常
     */
    @Operation(summary = "上传资源文件")
    @PreAuthorize("@ss.hasPermission('cms:tpl:create')")
    @PostMapping("/upload")
    public CommonResult upload(@RequestParam(value = "file", required = false) MultipartFile file,
                               String root, Boolean isCover, HttpServletRequest request) throws ServerException, IOException {
        validBlank(file);
        SiteDO site = CmsContextUtil.getSite();
        if (StringUtils.isBlank(root)) {
            root = site.getResourcePath();
        } else {
            FileUtil.isValidFilename(site.getResourcePath(), root);
        }
        //文件不合法
        validateFileFormat(file);
        validateFile(file);
        tplService.uploadTplResFile(root, file, isCover);
        return success(true);
    }

    @Resource
    private TplService tplService;
}
